{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "93f57ba8",
   "metadata": {
    "id": "introduction-cell"
   },
   "source": [
    "# React Agent\n",
    "\n",
    "- Author: [ranian963](https://github.com/ranian963)\n",
    "- Peer Review:\n",
    "- Proofread : [BokyungisaGod](https://github.com/BokyungisaGod)\n",
    "- This is part of the [LangChain Open Tutorial](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial)\n",
    "\n",
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/LangChain-OpenTutorial/LangChain-OpenTutorial/blob/main/15-Agent/11-React-Agent.ipynb)[![Open in GitHub](https://img.shields.io/badge/Open%20in%20GitHub-181717?style=flat-square&logo=github&logoColor=white)](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial/blob/main/15-Agent/11-React-Agent.ipynb)## Overview\n",
    "\n",
    "In this tutorial, we explore the concept and implementation of a **ReAct Agent**.\n",
    "\n",
    "**ReAct Agent** stands for Reasoning + Action, meaning that the LLM explicitly goes through a reasoning phase, utilizes tools (or actions), and then generates the final answer based on the obtained results.\n",
    "\n",
    "Throughout this tutorial, we will implement a ReAct Agent by covering the following:\n",
    "\n",
    "- **Tool Setup**: Utilizing various tools such as web search, file management, document search based on VectorStore, etc.\n",
    "- **Agent Creation**: Practice how to use the ReAct Agent in LangChain.\n",
    "- **Graph Execution**: Execute the agent to observe the answers to queries.\n",
    "\n",
    "Please follow the sections below to go through the entire process.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c94ab81c",
   "metadata": {
    "id": "table-of-contents"
   },
   "source": [
    "### Table of Contents\n",
    "\n",
    "- [Overview](#overview)\n",
    "- [Environment Setup](#environment-setup)\n",
    "- [Tools](#tools)\n",
    "  - [Web Search](#web-search)\n",
    "  - [File Management](#file-management)\n",
    "  - [Retriever Tool](#retriever-tool)\n",
    "- [Create and Visualize the Agent](#create-and-visualize-the-agent)\n",
    "- [Execute Agent](#execute-agent)\n",
    "\n",
    "### References\n",
    "- [ReAct: Synergizing Reasoning and Acting in Language Models (Yao et al.)](https://arxiv.org/abs/2210.03629)\n",
    "- [LangChain Official Documentation](https://langchain.readthedocs.io/en/latest/)\n",
    "- [LangChain-OpenTutorial GitHub](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial)\n",
    "----\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "037939d3",
   "metadata": {
    "id": "environment-setup"
   },
   "source": [
    "## Environment Setup\n",
    "\n",
    "Set up the environment. You may refer to [Environment Setup](https://wikidocs.net/257836) for more details.\n",
    "\n",
    "**[Note]**\n",
    "- ```langchain-opentutorial``` is a package that provides an easy-to-use environment setup, useful functions, and utilities for tutorials.\n",
    "- For more details, check out [```langchain-opentutorial```](https://github.com/LangChain-OpenTutorial/langchain-opentutorial-pypi).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9a9dd3da",
   "metadata": {
    "id": "install-requirements"
   },
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install langchain-opentutorial"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4038f6c8",
   "metadata": {
    "id": "langsmith-logging"
   },
   "outputs": [],
   "source": [
    "from langchain_opentutorial import package\n",
    "\n",
    "package.install(\n",
    "    [\n",
    "        \"langsmith\",\n",
    "        \"langchain\",\n",
    "        \"langchain_core\",\n",
    "        \"langchain_community\",\n",
    "        \"langchain_openai\",\n",
    "        \"langgraph\",\n",
    "        \"faiss-cpu\",\n",
    "        \"pymupdf\",\n",
    "    ],\n",
    "    verbose=False,\n",
    "    upgrade=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0ba52aca",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set environment variables\n",
    "from langchain_opentutorial import set_env\n",
    "\n",
    "set_env(\n",
    "    {\n",
    "        \"OPENAI_API_KEY\": \"\",\n",
    "        \"LANGCHAIN_API_KEY\": \"\",\n",
    "        \"LANGCHAIN_TRACING_V2\": \"true\",\n",
    "        \"LANGCHAIN_ENDPOINT\": \"https://api.smith.langchain.com\",\n",
    "        \"LANGCHAIN_PROJECT\": \"11-React-Agent\",\n",
    "        \"TAVILY_API_KEY\": \"\",\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7199797b",
   "metadata": {},
   "source": [
    "You can alternatively set ```OPENAI_API_KEY``` in a ```.env``` file and load it. \n",
    "\n",
    "[Note] This is not necessary if you've already set ```OPENAI_API_KEY``` previously."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40e81641",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "\n",
    "load_dotenv(override=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fcc5b8ab",
   "metadata": {},
   "source": [
    "**How to Set Up Tavily Search**\n",
    "\n",
    "- **Get an API Key**:\n",
    "\n",
    "  To use Tavily Search, you need an API key.\n",
    "\n",
    "  - [Generate your Tavily Search API key](https://app.tavily.com/sign-in)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "54b6d921",
   "metadata": {
    "id": "tools"
   },
   "source": [
    "## Tools\n",
    "\n",
    "A ReAct Agent uses various **tools** to solve problems. In this tutorial, we will set up and use the following tools.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77ccd5a6",
   "metadata": {
    "id": "web-search"
   },
   "source": [
    "### Web Search\n",
    "\n",
    "We use the ```TavilySearch``` tool for searching the latest information from the web.\n",
    "The code below creates a web search tool and tests it by retrieving some search results.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "92876d90",
   "metadata": {
    "id": "tavily-search"
   },
   "outputs": [],
   "source": [
    "from langchain_community.tools.tavily_search import TavilySearchResults\n",
    "\n",
    "# Create an instance of TavilySearchResults with k=5 for retrieving up to 5 search results\n",
    "web_search = TavilySearchResults(k=5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "71539a3c",
   "metadata": {
    "id": "test-web-search"
   },
   "outputs": [],
   "source": [
    "# Test the web search tool.\n",
    "result = web_search.invoke(\n",
    "    \"Please find information related to the major AI-related from CES 2025\"\n",
    ")\n",
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c2823f09",
   "metadata": {
    "id": "file-management"
   },
   "source": [
    "### File Management\n",
    "\n",
    "Using ```FileManagementToolkit```, you can create, delete, and modify files.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f6993809",
   "metadata": {
    "id": "file-management-tool"
   },
   "outputs": [],
   "source": [
    "from langchain_community.agent_toolkits import FileManagementToolkit\n",
    "\n",
    "# Set 'tmp' as the working directory.\n",
    "working_directory = \"tmp\"\n",
    "file_management_tools = FileManagementToolkit(\n",
    "    root_dir=str(working_directory)\n",
    ").get_tools()\n",
    "file_management_tools"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ea91f62f",
   "metadata": {
    "id": "retriever-tool"
   },
   "source": [
    "### Retriever Tool\n",
    "\n",
    "To search for information within documents such as PDFs, we create a Retriever. First, we load the PDF, split the text, then embed it into a VectorStore.\n",
    "\n",
    "**Tesla's Revenue Forecast Based on Business Model and Financial Statement Analysis**  \n",
    "\n",
    "**Author:** Chenhao Fang  \n",
    "**Institution:** Intelligent Accounting Management Institute, Guangdong University of Finance and Economics  \n",
    "**Link:** [Tesla's revenue forecast base on business model and financial statement analysis ](https://www.shs-conferences.org/articles/shsconf/pdf/2024/01/shsconf_icdeba2023_02022.pdf)  \n",
    "**File Name:** shsconf_icdeba2023_02022.pdf\n",
    "\n",
    "_Please copy the downloaded file to the data folder for practice._\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "704e1b16",
   "metadata": {
    "id": "retriever-creation"
   },
   "outputs": [],
   "source": [
    "from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
    "from langchain_community.vectorstores import FAISS\n",
    "from langchain_openai import OpenAIEmbeddings\n",
    "from langchain_community.document_loaders import PyMuPDFLoader\n",
    "\n",
    "# Example PDF file path (adjust according to your environment)\n",
    "pdf_file_path = \"data/shsconf_icdeba2023_02022.pdf\"\n",
    "\n",
    "# Load the PDF using PyMuPDFLoader\n",
    "loader = PyMuPDFLoader(pdf_file_path)\n",
    "\n",
    "# Split text into smaller chunks\n",
    "text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)\n",
    "split_docs = loader.load_and_split(text_splitter)\n",
    "\n",
    "# Create FAISS VectorStore\n",
    "vector = FAISS.from_documents(split_docs, OpenAIEmbeddings())\n",
    "\n",
    "# Create a retriever from the VectorStore\n",
    "pdf_retriever = vector.as_retriever()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0e85c2f2",
   "metadata": {
    "id": "retriever-tool-code"
   },
   "outputs": [],
   "source": [
    "from langchain_core.tools.retriever import create_retriever_tool\n",
    "from langchain_core.prompts import PromptTemplate\n",
    "\n",
    "# Create a tool for PDF-based search\n",
    "retriever_tool = create_retriever_tool(\n",
    "    retriever=pdf_retriever,\n",
    "    name=\"pdf_retriever\",\n",
    "    description=\"use this tool to search for information in Tesla PDF file\",\n",
    "    document_prompt=PromptTemplate.from_template(\n",
    "        \"<document><context>{page_content}</context><metadata><source>{source}</source><page>{page}</page></metadata></document>\"\n",
    "    ),\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ca548e5",
   "metadata": {
    "id": "define-tools"
   },
   "source": [
    "Combine these tools into a single list.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4be76a98",
   "metadata": {
    "id": "tools-list"
   },
   "outputs": [],
   "source": [
    "tools = [web_search, *file_management_tools, retriever_tool]\n",
    "tools"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a37a686d",
   "metadata": {
    "id": "create-and-visualize-the-agent"
   },
   "source": [
    "## Create and Visualize the Agent\n",
    "\n",
    "We will now create a ReAct Agent and visualize the agent graph.\n",
    "In LangChain, a **ReAct** agent generates answers through step-by-step reasoning and tool usage.\n",
    "\n",
    "The code below uses ```create_react_agent``` from **langgraph** to easily build a ReAct Agent and visualize its structure as a graph.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12d4f632",
   "metadata": {
    "id": "create-agent"
   },
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "from langgraph.prebuilt import create_react_agent\n",
    "\n",
    "# Memory and model configuration\n",
    "memory = MemorySaver()\n",
    "model = ChatOpenAI(model_name=\"gpt-4o-mini\")\n",
    "\n",
    "# Create ReAct Agent\n",
    "agent = create_react_agent(model, tools=tools, checkpointer=memory)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4772db6",
   "metadata": {
    "id": "visualize-agent"
   },
   "source": [
    "The code below visualizes the agent's graph structure.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d587559d",
   "metadata": {
    "id": "viz-agent"
   },
   "outputs": [],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "display(\n",
    "    Image(agent.get_graph().draw_mermaid_png(output_file_path=\"11-React-Agent.png\"))\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aa2fc4c4",
   "metadata": {},
   "outputs": [],
   "source": [
    "def print_stream(stream):\n",
    "    for s in stream:\n",
    "        message = s[\"messages\"][-1]\n",
    "        if isinstance(message, tuple):\n",
    "            print(message)\n",
    "        else:\n",
    "            message.pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e21d9a6",
   "metadata": {
    "id": "execute-agent"
   },
   "source": [
    "## Execute Agent\n",
    "\n",
    "Let's execute the created ReAct Agent. We can track the step-by-step process of generating an answer to a query.\n",
    "\n",
    "The example code below uses ```stream_graph``` to stream the agent's execution process. We place the user's query in the ```messages``` key, and the agent's reasoning will be displayed.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6558f303",
   "metadata": {
    "id": "execute-agent-simple"
   },
   "outputs": [],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "inputs = {\"messages\": [(\"human\", \"Hello, my name is Teddy.\")]}\n",
    "\n",
    "print_stream(agent.stream(inputs, config=config, stream_mode=\"values\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5ac56fa8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Another example - maintaining chat flow\n",
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "inputs = {\"messages\": [(\"human\", \"What was my name again?\")]}\n",
    "\n",
    "print_stream(agent.stream(inputs, config=config, stream_mode=\"values\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "75043007",
   "metadata": {},
   "outputs": [],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "inputs = {\n",
    "    \"messages\": [(\"human\", \"Please summarize Tesla from shsconf_icdeba2023_02022.pdf.\")]\n",
    "}\n",
    "\n",
    "print_stream(agent.stream(inputs, config=config, stream_mode=\"values\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18191653",
   "metadata": {
    "id": "another-example"
   },
   "outputs": [],
   "source": [
    "# Example of using web search + file management\n",
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "inputs = {\n",
    "    \"messages\": [\n",
    "        (\n",
    "            \"human\",\n",
    "            \"Search for news about American writer John Smith's Pulitzer Prize and draft a brief report based on it.\",\n",
    "        )\n",
    "    ]\n",
    "}\n",
    "\n",
    "print_stream(agent.stream(inputs, config=config, stream_mode=\"values\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "74449909",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A more concrete scenario example\n",
    "instruction = \"\"\"\n",
    "Your task is to write a 'press release.'\n",
    "----\n",
    "Please process the following steps in order:\n",
    "1. Search for news about American writer John Smith's Pulitzer Prize.\n",
    "2. Based on the Pulitzer Prize news, write a press release/report.\n",
    "3. Actively use markdown table format to summarize key points.\n",
    "4. Save the output to a file named `agent_press_release.md`.\n",
    "\"\"\"\n",
    "\n",
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "inputs = {\"messages\": [(\"human\", instruction)]}\n",
    "print_stream(agent.stream(inputs, config=config, stream_mode=\"values\"))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": ""
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
